/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.auth.check;

import com.je.auth.check.annotation.*;
import com.je.auth.check.exception.*;
import com.je.auth.check.util.FoxUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.je.auth.check.exception.NotLoginException.DEFAULT_MESSAGE;

public interface AuthCheckManager {

    /**
     * 设置check engine
     *
     * @param authCheckEngine
     * @return
     */
    void setAuthCheckEngine(AuthCheckEngine authCheckEngine);

    /**
     * 获取check engine
     *
     * @return
     */
    AuthCheckEngine getAuthCheckEngine();

    /**
     * 检验当前会话是否已经登录，如未登录，则抛出异常
     */
    default void checkLogin() {
        if(!getAuthCheckEngine().getRbacInterface().isLogin()){
            throw NotLoginException.newInstance(DEFAULT_MESSAGE);
        }
    }

    // ------------------- 机构验证操作 -------------------

    /**
     * 获取：当前账号的机构集合
     *
     * @return /
     */
    default List<String> getOrgList() {
        try {
            return getOrgList(getAuthCheckEngine().getRbacInterface().getLoginId());
        } catch (NotOrgException e) {
            return FoxUtil.emptyList();
        }
    }

    /**
     * 获取：指定账号的机构集合
     *
     * @param loginId 指定账号id
     * @return /
     */
    default List<String> getOrgList(Object loginId) {
        return getAuthCheckEngine().getRbacInterface().getOrgList(loginId);
    }

    /**
     * 判断：当前账号是否拥有指定机构, 返回true或false
     *
     * @param org 机构
     * @return /
     */
    default boolean hasOrg(String org) {
        return hasElement(getOrgList(), org);
    }

    /**
     * 判断：指定账号是否含有指定机构标识, 返回true或false
     *
     * @param loginId 账号id
     * @param org     机构标识
     * @return 是否含有指定机构标识
     */
    default boolean hasOrg(Object loginId, String org) {
        return hasElement(getOrgList(loginId), org);
    }

    /**
     * 判断：当前账号是否含有指定机构标识 [指定多个，必须全部验证通过]
     *
     * @param orgArray 机构标识数组
     * @return true或false
     */
    default boolean hasOrgAnd(String... orgArray) {
        try {
            checkOrgAnd(orgArray);
            return true;
        } catch (NotLoginException | NotOrgException e) {
            return false;
        }
    }

    /**
     * 判断：当前账号是否含有指定机构标识 [指定多个，只要其一验证通过即可]
     *
     * @param orgArray 机构标识数组
     * @return true或false
     */
    default boolean hasOrgOr(String... orgArray) {
        try {
            checkOrgOr(orgArray);
            return true;
        } catch (NotLoginException | NotOrgException e) {
            return false;
        }
    }

    /**
     * 校验：当前账号是否含有指定机构标识, 如果验证未通过，则抛出异常: NotDeptException
     *
     * @param org 机构标识
     */
    default void checkOrg(String org) {
        if (hasOrg(org) == false) {
            throw new NotOrgException(org);
        }
    }

    /**
     * 校验：当前账号是否含有指定机构标识 [指定多个，必须全部验证通过]
     *
     * @param orgArray 机构标识数组
     */
    default void checkOrgAnd(String... orgArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        List<String> orgList = getOrgList(loginId);
        for (String org : orgArray) {
            if (!hasElement(orgList, org)) {
                throw new NotOrgException(org);
            }
        }
    }

    /**
     * 校验：当前账号是否含有指定机构标识 [指定多个，只要其一验证通过即可]
     *
     * @param orgArray 机构标识数组
     */
    default void checkOrgOr(String... orgArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        List<String> orgList = getOrgList(loginId);
        for (String org : orgArray) {
            if (hasElement(orgList, org)) {
                // 有的话提前退出
                return;
            }
        }
        if (orgArray.length > 0) {
            throw new NotOrgException(orgArray[0]);
        }
    }

    // ------------------- 部门验证操作 -------------------

    /**
     * 获取：当前账号的部门集合
     *
     * @return /
     */
    default List<String> getDeptList() {
        try {
            return getDeptList(getAuthCheckEngine().getRbacInterface().getLoginId(),getAuthCheckEngine().getRbacInterface().getTenantId());
        } catch (NotLoginException e) {
            return FoxUtil.emptyList();
        }
    }

    /**
     * 获取：指定账号的部门集合
     *
     * @param loginId 指定账号id
     * @param tenantId
     * @return /
     */
    default List<String> getDeptList(Object loginId,String tenantId) {
        return getAuthCheckEngine().getRbacInterface().getDeptList(loginId,tenantId);
    }

    /**
     * 判断：当前账号是否拥有指定部门, 返回true或false
     *
     * @param dept 部门
     * @return /
     */
    default boolean hasDept(String dept) {
        return hasElement(getDeptList(), dept);
    }

    /**
     * 判断：指定账号是否含有指定部门标识, 返回true或false
     *
     * @param loginId 账号id
     * @param dept    部门标识
     * @return 是否含有指定部门标识
     */
    default boolean hasDept(Object loginId,String tenantId, String dept) {
        return hasElement(getDeptList(loginId,tenantId), dept);
    }

    /**
     * 判断：当前账号是否含有指定部门标识 [指定多个，必须全部验证通过]
     *
     * @param deptArray 部门标识数组
     * @return true或false
     */
    default boolean hasDeptAnd(String... deptArray) {
        try {
            checkDeptAnd(deptArray);
            return true;
        } catch (NotLoginException | NotDeptException e) {
            return false;
        }
    }

    /**
     * 判断：当前账号是否含有指定部门标识 [指定多个，只要其一验证通过即可]
     *
     * @param deptArray 部门标识数组
     * @return true或false
     */
    default boolean hasDeptOr(String... deptArray) {
        try {
            checkDeptOr(deptArray);
            return true;
        } catch (NotLoginException | NotDeptException e) {
            return false;
        }
    }

    /**
     * 校验：当前账号是否含有指定部门标识, 如果验证未通过，则抛出异常: NotDeptException
     *
     * @param dept 部门标识
     */
    default void checkDept(String dept) {
        if (hasDept(dept) == false) {
            throw new NotDeptException(dept);
        }
    }

    /**
     * 校验：当前账号是否含有指定部门标识 [指定多个，必须全部验证通过]
     *
     * @param deptArray 部门标识数组
     */
    default void checkDeptAnd(String... deptArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
        List<String> deptList = getDeptList(loginId,tenantId);
        for (String dept : deptArray) {
            if (!hasElement(deptList, dept)) {
                throw new NotDeptException(dept);
            }
        }
    }

    /**
     * 校验：当前账号是否含有指定部门标识 [指定多个，只要其一验证通过即可]
     *
     * @param deptArray 部门标识数组
     */
    default void checkDeptOr(String... deptArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
        List<String> deptList = getDeptList(loginId,tenantId);
        for (String dept : deptArray) {
            if (hasElement(deptList, dept)) {
                // 有的话提前退出
                return;
            }
        }
        if (deptArray.length > 0) {
            throw new NotDeptException(deptArray[0]);
        }
    }

    // ------------------- 角色验证操作 -------------------

    /**
     * 获取：当前账号的角色集合
     *
     * @return /
     */
    default List<String> getRoleList() {
        try {
            return getRoleList(getAuthCheckEngine().getRbacInterface().getLoginId(),getAuthCheckEngine().getRbacInterface().getTenantId());
        } catch (NotLoginException e) {
            return FoxUtil.emptyList();
        }
    }

    /**
     * 获取：指定账号的角色集合
     *
     * @param loginId 指定账号id
     * @return /
     */
    default List<String> getRoleList(Object loginId,String tenantId) {
        return getAuthCheckEngine().getRbacInterface().getRoleList(loginId,tenantId);
    }

    /**
     * 判断：当前账号是否拥有指定角色, 返回true或false
     *
     * @param role 角色
     * @return /
     */
    default boolean hasRole(String role) {
        return hasElement(getRoleList(), role);
    }

    /**
     * 判断：指定账号是否含有指定角色标识, 返回true或false
     *
     * @param loginId 账号id
     * @param role    角色标识
     * @return 是否含有指定角色标识
     */
    default boolean hasRole(Object loginId,String tenantId, String role) {
        return hasElement(getRoleList(loginId,tenantId), role);
    }

    /**
     * 判断：当前账号是否含有指定角色标识 [指定多个，必须全部验证通过]
     *
     * @param roleArray 角色标识数组
     * @return true或false
     */
    default boolean hasRoleAnd(String... roleArray) {
        try {
            checkRoleAnd(roleArray);
            return true;
        } catch (NotLoginException | NotRoleException e) {
            return false;
        }
    }

    /**
     * 判断：当前账号是否含有指定角色标识 [指定多个，只要其一验证通过即可]
     *
     * @param roleArray 角色标识数组
     * @return true或false
     */
    default boolean hasRoleOr(String... roleArray) {
        try {
            checkRoleOr(roleArray);
            return true;
        } catch (NotLoginException | NotRoleException e) {
            return false;
        }
    }

    /**
     * 校验：当前账号是否含有指定角色标识, 如果验证未通过，则抛出异常: NotRoleException
     *
     * @param role 角色标识
     */
    default void checkRole(String role) {
        if (hasRole(role) == false) {
            throw new NotRoleException(role);
        }
    }

    /**
     * 校验：当前账号是否含有指定角色标识 [指定多个，必须全部验证通过]
     *
     * @param roleArray 角色标识数组
     */
    default void checkRoleAnd(String... roleArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
        List<String> roleList = getRoleList(loginId,tenantId);
        for (String role : roleArray) {
            if (!hasElement(roleList, role)) {
                throw new NotRoleException(role);
            }
        }
    }

    /**
     * 校验：当前账号是否含有指定角色标识 [指定多个，只要其一验证通过即可]
     *
     * @param roleArray 角色标识数组
     */
    default void checkRoleOr(String... roleArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
        List<String> roleList = getRoleList(loginId,tenantId);
        for (String role : roleArray) {
            if (hasElement(roleList, role)) {
                // 有的话提前退出
                return;
            }
        }
        if (roleArray.length > 0) {
            throw new NotRoleException(roleArray[0]);
        }
    }

    // ------------------- 权限验证操作 -------------------

    /**
     * 获取：当前账号的权限码集合
     *
     * @return /
     */
    default List<String> getPermissionList() {
        try {
            Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
            String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
            return getPermissionList(loginId,tenantId);
        } catch (NotLoginException e) {
            return FoxUtil.emptyList();
        }
    }

    /**
     * 获取：指定账号的权限码集合
     *
     * @param loginId 指定账号id
     * @return /
     */
    default List<String> getPermissionList(Object loginId,String tenantId) {
        return getAuthCheckEngine().getRbacInterface().getPermissionList(loginId,tenantId);
    }

    /**
     * 判断：当前账号是否含有指定权限, 返回true或false
     *
     * @param permission 权限码
     * @return 是否含有指定权限
     */
    default boolean hasPermission(String permission) {
        return hasElement(getPermissionList(), permission);
    }

    /**
     * 判断：指定账号id是否含有指定权限, 返回true或false
     *
     * @param loginId    账号id
     * @param permission 权限码
     * @return 是否含有指定权限
     */
    default boolean hasPermission(Object loginId,String tenantId, String permission) {
        return hasElement(getPermissionList(loginId,tenantId), permission);
    }

    /**
     * 判断：当前账号是否含有指定权限, [指定多个，必须全部具有]
     *
     * @param permissionArray 权限码数组
     * @return true 或 false
     */
    default boolean hasPermissionAnd(String... permissionArray) {
        try {
            checkPermissionAnd(permissionArray);
            return true;
        } catch (NotLoginException | NotPermissionException e) {
            return false;
        }
    }

    /**
     * 判断：当前账号是否含有指定权限 [指定多个，只要其一验证通过即可]
     *
     * @param permissionArray 权限码数组
     * @return true 或 false
     */
    default boolean hasPermissionOr(String... permissionArray) {
        try {
            checkPermissionOr(permissionArray);
            return true;
        } catch (NotLoginException | NotPermissionException e) {
            return false;
        }
    }

    /**
     * 校验：当前账号是否含有指定权限, 如果验证未通过，则抛出异常: NotPermissionException
     *
     * @param permission 权限码
     */
    default void checkPermission(String permission) {
        if (hasPermission(permission) == false) {
            throw new NotPermissionException(permission);
        }
    }

    /**
     * 校验：当前账号是否含有指定权限 [指定多个，必须全部验证通过]
     *
     * @param permissionArray 权限码数组
     */
    default void checkPermissionAnd(String... permissionArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
        List<String> permissionList = getPermissionList(loginId,tenantId);
        for (String permission : permissionArray) {
            if (!hasElement(permissionList, permission)) {
                throw new NotPermissionException(permission);
            }
        }
    }

    /**
     * 校验：当前账号是否含有指定权限 [指定多个，只要其一验证通过即可]
     *
     * @param permissionArray 权限码数组
     */
    default void checkPermissionOr(String... permissionArray) {
        Object loginId = getAuthCheckEngine().getRbacInterface().getLoginId();
        String tenantId = getAuthCheckEngine().getRbacInterface().getTenantId();
        List<String> permissionList = getPermissionList(loginId,tenantId);
        for (String permission : permissionArray) {
            if (hasElement(permissionList, permission)) {
                // 有的话提前退出
                return;
            }
        }
        if (permissionArray.length > 0) {
            throw new NotPermissionException(permissionArray[0]);
        }
    }

    // ------------------- 注解鉴权 -------------------

    /**
     * 根据注解(@CheckLogin)鉴权
     *
     * @param at 注解对象
     */
    default void checkByAnnotation(AuthCheckLogin at) {
        this.checkLogin();
    }

    /**
     * 根据注解(@SaCheckRole)鉴权
     *
     * @param at 注解对象
     */
    default void checkByAnnotation(AuthCheckRole at) {
        String[] roleArray = at.value();
        if (at.mode() == AuthMode.AND) {
            this.checkRoleAnd(roleArray);
        } else {
            this.checkRoleOr(roleArray);
        }
    }

    /**
     * 根据注解(@CheckOrg)鉴权
     *
     * @param at 注解对象
     */
    default void checkByAnnotation(AuthCheckOrg at) {
        String[] orgArray = at.value();
        if (at.mode() == AuthMode.AND) {
            this.checkOrgAnd(orgArray);
        } else {
            this.checkOrgOr(orgArray);
        }
    }

    /**
     * 根据注解(@CheckOrg)鉴权
     *
     * @param at 注解对象
     */
    default void checkByAnnotation(AuthCheckDept at) {
        String[] deptArray = at.value();
        if (at.mode() == AuthMode.AND) {
            this.checkDeptAnd(deptArray);
        } else {
            this.checkDeptOr(deptArray);
        }
    }

    /**
     * 根据注解(@SaCheckPermission)鉴权
     *
     * @param at 注解对象
     */
    default void checkByAnnotation(AuthCheckPermission at) {
        String[] permissionArray = at.value();
        try {
            if (at.mode() == AuthMode.AND) {
                this.checkPermissionAnd(permissionArray);
            } else {
                this.checkPermissionOr(permissionArray);
            }
        } catch (NotPermissionException e) {
            // 权限认证未通过，再开始角色认证
            if (at.orRole().length > 0) {
                for (String role : at.orRole()) {
                    String[] rArr = FoxUtil.convertStringToArray(role);
                    // 某一项role认证通过，则可以提前退出了，代表通过
                    if (hasRoleAnd(rArr)) {
                        return;
                    }
                }
            }
            throw e;
        }
    }

    /**
     * 根据注解(@SaCheckPermission)鉴权
     *
     * @param at 注解对象
     */
    default void checkByAnnotation(AuthCheckPermissionTemplate at) {
        String[] permissionTeplateArray = at.template();
        String[] templateParams = at.params();
        String[] permissionArray;
        if(templateParams == null || templateParams.length <= 0){
            permissionArray = permissionTeplateArray;
        }else {
            permissionArray = new String[permissionTeplateArray.length];
            Map<String,String> paramsMap = new HashMap<>();
            for (String eachParam : templateParams) {
                paramsMap.put(eachParam,getAuthCheckEngine().getAuthTokenContext().getRequest().getParam(eachParam));
            }
            for (int i = 0; i < permissionTeplateArray.length; i++) {
                permissionArray[i] = FoxUtil.tempalteMatch(permissionTeplateArray[i],paramsMap);
            }
        }
        try {
            if (at.mode() == AuthMode.AND) {
                this.checkPermissionAnd(permissionArray);
            } else {
                this.checkPermissionOr(permissionArray);
            }
        } catch (NotPermissionException e) {
            // 权限认证未通过，再开始角色认证
            if (at.orRole().length > 0) {
                for (String role : at.orRole()) {
                    String[] rArr = FoxUtil.convertStringToArray(role);
                    // 某一项role认证通过，则可以提前退出了，代表通过
                    if (hasRoleAnd(rArr)) {
                        return;
                    }
                }
            }
            throw e;
        }
    }

    /**
     * 判断：集合中是否包含指定元素（模糊匹配）
     *
     * @param list    集合
     * @param element 元素
     * @return /
     */
    default boolean hasElement(List<String> list, String element) {
        return getAuthCheckEngine().getAuthCheckStrategy().hasElement.apply(list, element);
    }

}
